package bestTeam.WalkieDoggie.server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;

import sun.security.action.GetLongAction;

import bestTeam.WalkieDoggie.client.Location;
import bestTeam.WalkieDoggie.client.NotLoggedInException;
import bestTeam.WalkieDoggie.client.Serializables.UserProfileData;
import bestTeam.WalkieDoggie.client.Serializables.UserRequest;
import bestTeam.WalkieDoggie.client.ServicesInterfaces.RequestService;
import bestTeam.WalkieDoggie.server.Persistant.AppUser;
import bestTeam.WalkieDoggie.server.Persistant.PersistantRequest;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class RequestServiceImpl extends RemoteServiceServlet implements
RequestService {

	private double MAX_DISTANCE = 0.5; //KM

	private static final Logger LOG = Logger.getLogger(RequestServiceImpl.class
			.getName());
	private static final PersistenceManagerFactory PMF = PersistanceManager
			.getPMF();

	@Override
	public String addRequest(UserRequest request){
		//checkLoggedIn();
		PersistenceManager pm = getPersistenceManager();

		try {

			//request.setId(createKey(request));
			PersistantRequest newRequest = new PersistantRequest(request);

			Key key = createKey(request);
			newRequest.setId(key);
			pm.makePersistent(newRequest);

			return KeyFactory.keyToString(newRequest.getId());
		} catch (Exception e) {
			return null;
		} finally {
			pm.close();
		}

	}

	private Key createKey(UserRequest request) {
		String id = request.getCreatedBy();
		Long time = request.getDate().getTime();

		Key key = KeyFactory.createKey(PersistantRequest.class.getSimpleName(), id+time);

		return key;
	}

	@Override
	public void removeRequest(String id){
		//checkLoggedIn();
		PersistenceManager pm = getPersistenceManager();
		try {
			long deleteCount = 0;

			Query q = pm.newQuery(PersistantRequest.class);
			q.setFilter("rid == id");

			q.declareParameters("String id");

			// User u = getUser();
			List<PersistantRequest> userRequests = (List<PersistantRequest>) q.execute(id);


			for (PersistantRequest userRequest : userRequests) {
				if (id.equals(KeyFactory.keyToString(userRequest.getId()))) {
					deleteCount++;
					pm.deletePersistent(userRequest);
				}
			}
			if (deleteCount != 1) {
				LOG.log(Level.WARNING, "removeUserRequest deleted "
						+ deleteCount + " Requests");
			}
		} catch (Exception e) {

		} finally {
			pm.close();
		}

	}


	// 0 is for my walkies.  everything else is for walkies list
	private UserRequest[] getRequests(String uid){

		PersistenceManager pm = getPersistenceManager();
		List<UserRequest> res = new ArrayList<UserRequest>();
		Location myLocation = getGeoLocation(uid);

		try {
			Query q = pm.newQuery(PersistantRequest.class);
			//			q.setFilter("createdBy != uid");
			//
			//			q.declareParameters("String uid");

			List<PersistantRequest> walkieRequests = (List<PersistantRequest>) q.execute();

			if (walkieRequests != null) {

				for (PersistantRequest walkieRequest : walkieRequests) {

					//check if the request is not too far. if its in the range, we will add it to the result
					Location otherLocation = getGeoLocation(walkieRequest.getCreatedBy());
					double distance = getDistance(myLocation.getLat(),myLocation.getLng(),otherLocation.getLat(),otherLocation.getLng());

					if(distance<=MAX_DISTANCE){
						// need to duplicate the entity to be serialized object we
						// can send to client
						UserRequest request = walkieRequestToUserRequest(walkieRequest);

						res.add(request);
					}
				}
			}
		} catch (Exception e) {	
			e.printStackTrace();
			return null;
		} finally {
			pm.close();
		}
		return (UserRequest[]) res.toArray(new UserRequest[0]);


	}

	private UserRequest[] walkiesList(String uid) {
		PersistenceManager pm = getPersistenceManager();
		List<UserRequest> res = new ArrayList<UserRequest>();
		Location myLocation = getGeoLocation(uid);

		try {
			Query q = pm.newQuery(PersistantRequest.class);
			q.setFilter("createdBy != uid");

			q.declareParameters("String uid");
			List<PersistantRequest> walkieRequests = (List<PersistantRequest>) q.execute(uid);

			if (walkieRequests != null) {

				for (PersistantRequest walkieRequest : walkieRequests) {

					//check if the request is not too far. if its in the range, we will add it to the result
					Location otherLocation = getGeoLocation(walkieRequest.getCreatedBy());
					double distance = getDistance(myLocation.getLat(),myLocation.getLng(),otherLocation.getLat(),otherLocation.getLng());

					if(distance<=MAX_DISTANCE){
						// need to duplicate the entity to be serialized object we
						// can send to client
						UserRequest request = walkieRequestToUserRequest(walkieRequest);

						res.add(request);
					}
				}
			}
		} catch (Exception e) {	
			e.printStackTrace();
			return null;
		} finally {
			pm.close();
		}
		return (UserRequest[]) res.toArray(new UserRequest[0]);

	}

	private UserRequest[] myWalkies(String uid) {		PersistenceManager pm = getPersistenceManager();
		List<UserRequest> res = new ArrayList<UserRequest>();

		try {
			Query q = pm.newQuery(PersistantRequest.class);
			q.setFilter("createdBy == uid");
			q.declareParameters("String uid");

			List<PersistantRequest> walkieRequests = (List<PersistantRequest>) q.execute(uid);

			if (walkieRequests != null) {

				for (PersistantRequest walkieRequest : walkieRequests) {

					// need to duplicate the entity to be serialized object we
					// can send to client
					UserRequest request = walkieRequestToUserRequest(walkieRequest);

					res.add(request);
				}
			}

		} catch (Exception e) {	
		} finally {
			pm.close();
		}
		return (UserRequest[]) res.toArray(new UserRequest[0]);

	}

	private double getDistance(double lat1, double lon1, double lat2, double lon2) {
		double theta = lon1 - lon2;
		double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
		dist = Math.acos(dist);
		dist = rad2deg(dist);
		dist = dist * 60 * 1.1515;

		dist = dist * 1.609344;

		return (dist);
	}


	/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
	/*::  This function converts decimal degrees to radians             :*/
	/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
	private double deg2rad(double deg) {
		return (deg * Math.PI / 180.0);
	}

	/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
	/*::  This function converts radians to decimal degrees             :*/
	/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
	private double rad2deg(double rad) {
		return (rad * 180.0 / Math.PI);
	}

	private Location getGeoLocation(String uid) {
		FacebookUsersServiceImpl usersService = new FacebookUsersServiceImpl();
		AppUser user = usersService.getAppUser(uid);

		if(user==null)
			return null;

		Location location = new Location(user.getLat(), user.getLng());

		return location;
	}

	@Override
	// if flag == 0, the method will return current user request, else, the
	// method will return all other
	public HashMap<UserRequest, UserProfileData> getRequestsAndOwners(String uid){

		UserRequest[] requests = getRequests(uid);
		if(requests==null)
			return null;
		String[] usersIds = new String[requests.length];
		for (int i=0 ; i<requests.length ; i++){
			usersIds[i] = requests[i].getCreatedBy();
		}

		FacebookUsersServiceImpl a = new FacebookUsersServiceImpl();
		UserProfileData[] users = a.getUsers(usersIds);
		HashMap<UserRequest,UserProfileData> map = buildMapFromValues(requests,users);

		return map;
	}




	private HashMap<UserRequest, UserProfileData> buildMapFromValues(UserRequest[] requests, UserProfileData[] users) {
		HashMap<UserRequest,UserProfileData> res = new HashMap<UserRequest, UserProfileData>();

		for (int i=0 ; i<requests.length ; i++){
			res.put(requests[i], users[i]);
		}

		return res;

	}

	private UserRequest walkieRequestToUserRequest(PersistantRequest persistantRequest) {

		UserRequest request = new UserRequest();

		request.setUserName(persistantRequest.getUserName());
		request.setDate(persistantRequest.getWalkTime());
		request.setId(KeyFactory.keyToString(persistantRequest.getId()));
		request.setMessage(persistantRequest.getMessage());
		request.setTimeFrame(persistantRequest.getTimeFrame());
		if (persistantRequest.getVolunteers() != null) {
			request.setVolunteers((String[]) persistantRequest.getVolunteers()
					.toArray(new String[0]));
		} else
			request.setVolunteers(null);

		request.setRank(persistantRequest.getRank());
		request.setTimeFrame(persistantRequest.getTimeFrame());
		request.setCreatedBy(persistantRequest.getCreatedBy());
		request.setApprovedWalkerID(persistantRequest.getApprovedWalkerID());
		request.setStatus(persistantRequest.getStatus());
		return request;

	}

	//	@Override
	//	public void confirmRequest(String requestId, String userId) throws NotLoggedInException {
	//
	//
	//		//checkLoggedIn();
	//		PersistenceManager pm = getPersistenceManager();
	//
	//		Key key = KeyFactory.stringToKey(requestId);
	//		Query q = pm.newQuery(PersistantRequest.class, "rid == id");
	//		q.declareParameters("com.google.appengine.api.datastore.Key id");
	//		List<PersistantRequest> requests = (List<PersistantRequest>) q
	//				.execute(key);
	//		try {
	//			pm.currentTransaction().begin();
	//			for (PersistantRequest request : requests) {
	//
	//				ArrayList<String> currentVolunteers = request.getVolunteers();
	//				if (currentVolunteers != null)
	//					currentVolunteers.add(userId);
	//
	//				else {
	//					currentVolunteers = new ArrayList<String>();
	//					currentVolunteers.add(userId);
	//				}
	//				if (request.getStatus() == walkieStatus.NEW)
	//					request.setStatus(walkieStatus.HAS_VOLUNTEERS);
	//				request.setVolunteers(currentVolunteers);
	//			}
	//			pm.currentTransaction().commit();
	//		} catch (Exception e) {
	//			pm.currentTransaction().rollback();
	//		} finally {
	//			pm.close();
	//		}
	//
	//	}
	//
	//	@Override
	//	public void UNconfirmRequest(String requestId,String userId) throws NotLoggedInException {
	//		//checkLoggedIn();
	//		PersistenceManager pm = getPersistenceManager();
	//
	//		Key key = KeyFactory.stringToKey(requestId);
	//		Query q = pm.newQuery(PersistantRequest.class, "rid == id");
	//		q.declareParameters("com.google.appengine.api.datastore.Key id");
	//		List<PersistantRequest> requests = (List<PersistantRequest>) q.execute(key);
	//		try {
	//			pm.currentTransaction().begin();
	//			for (PersistantRequest request : requests) {
	//				ArrayList<String> currentVolunteers = request.getVolunteers();
	//				if (currentVolunteers == null)
	//					return;
	//
	//
	//				currentVolunteers.remove(currentVolunteers.indexOf(userId));
	//
	//				request.setVolunteers(currentVolunteers);
	//				if (currentVolunteers.size() == 0) {
	//					request.setStatus(walkieStatus.NEW);
	//				}
	//			}
	//			pm.currentTransaction().commit();
	//		} catch (Exception e) {
	//			pm.currentTransaction().rollback();
	//
	//		} finally {
	//			pm.close();
	//		}
	//
	//	}

	private PersistenceManager getPersistenceManager() {
		return PMF.getPersistenceManager();
	}

	@Override
	public void UpdateRequest(UserRequest userRequest) {
		PersistenceManager pm = getPersistenceManager();
		Key key = KeyFactory.stringToKey(userRequest.getId());
		Query q = pm.newQuery(PersistantRequest.class, "rid == id");
		q.declareParameters("com.google.appengine.api.datastore.Key id");
		List<PersistantRequest> requests = (List<PersistantRequest>) q.execute(key);

		try {
			pm.currentTransaction().begin();
			for (PersistantRequest request : requests) {
				updateRequest(request,userRequest); 
			}
			pm.currentTransaction().commit();

		} catch (Exception e) {
			pm.currentTransaction().rollback();
		} finally {
			pm.close();
		}
	}

	private void updateRequest(PersistantRequest request,UserRequest userRequest) {
		request.setMessage(userRequest.getMessage());
		request.setStatus(userRequest.getStatus());
		request.setTimeFrame(userRequest.getTimeFrame());
		request.setVolunteers(ArrayToArrayList(userRequest.getVolunteers()));
		request.setWalkTime(userRequest.getDate());
		request.setApprovedWalkerID(userRequest.getApprovedWalkerID());
		request.setRank(userRequest.getRank());

	}

	private ArrayList<String> ArrayToArrayList(String[] volunteers) {
		ArrayList<String> res = new ArrayList<String>();

		for (String string : volunteers) {
			res.add(string);
		}

		return res;
	}

	@Override
	public HashMap<UserRequest, UserProfileData> getRequestsAndOwners(){

		UserRequest[] requests = getRequests();
		String[] usersIds = new String[requests.length];
		for (int i=0 ; i<requests.length ; i++){
			usersIds[i] = requests[i].getCreatedBy();
		}

		FacebookUsersServiceImpl a = new FacebookUsersServiceImpl();
		UserProfileData[] users = a.getUsers(usersIds);
		HashMap<UserRequest,UserProfileData> map = buildMapFromValues(requests,users);

		return map;
	}

	private UserRequest[] getRequests() {

		PersistenceManager pm = getPersistenceManager();
		List<UserRequest> res = new ArrayList<UserRequest>();

		try {
			Query q = pm.newQuery(PersistantRequest.class);

			List<PersistantRequest> walkieRequests = (List<PersistantRequest>) q.execute();

			if (walkieRequests != null) {

				for (PersistantRequest walkieRequest : walkieRequests) {

					// need to duplicate the entity to be serialized object we
					// can send to client
					UserRequest request = walkieRequestToUserRequest(walkieRequest);

					res.add(request);

				}
			}
		} catch (Exception e) {	
			e.printStackTrace();
			return null;
		} finally {
			pm.close();
		}
		return (UserRequest[]) res.toArray(new UserRequest[0]);

	}

	@Override
	public UserRequest getRequest(String requestID) {

		PersistenceManager pm = getPersistenceManager();
		UserRequest userRequest = null;

		try {
			Query q = pm.newQuery(PersistantRequest.class);
			q.setFilter("rid == requestID");
			q.declareParameters("String requestID");

			List<PersistantRequest> walkieRequests = (List<PersistantRequest>) q.execute(requestID);

			if (walkieRequests != null){
				userRequest = walkieRequestToUserRequest(walkieRequests.get(0));
			}
			
		} catch (Exception e) {	
			e.printStackTrace();
			return null;
		} finally {
			pm.close();
		}
		return userRequest;


	}

	//	@Override
	//	public void setStatus(String id, walkieStatus status) {
	//
	//
	//		//checkLoggedIn();
	//
	//		PersistenceManager pm = getPersistenceManager();
	//		Key key = KeyFactory.stringToKey(id);
	//		Query q = pm.newQuery(PersistantRequest.class, "rid == id");
	//		q.declareParameters("com.google.appengine.api.datastore.Key id");
	//		List<PersistantRequest> requests = (List<PersistantRequest>) q.execute(key);
	//
	//		try {
	//			pm.currentTransaction().begin();
	//			for (PersistantRequest request : requests) {
	//				if (request.getStatus() == walkieStatus.HAS_VOLUNTEERS)
	//					request.setStatus(status);
	//			}
	//			pm.currentTransaction().commit();
	//
	//		} catch (Exception e) {
	//			pm.currentTransaction().rollback();
	//		} finally {
	//			pm.close();
	//		}
	//	}

}